convert ozi io to QTextStream (#315)
authortsteven4 <tsteven4@users.noreply.github.com>
Sun, 10 Mar 2019 15:10:36 +0000 (09:10 -0600)
committerGitHub <noreply@github.com>
Sun, 10 Mar 2019 15:10:36 +0000 (09:10 -0600)
* convert ozi io to QTextStream.

and add an option to set the codec.
default the codec to windows-1252, which matches historic
usage but not recent behavior.

fix a memory leak, csv_lineparse needs to die.

fix a mistranslation with QString::arg.

* enhance ozi test for routes and tracks.

* eliminate csv_lineparse in ozi.

* eliminate obsolete commented code in ozi.

* update encoding comments for ozi.

* add doc for ozi codec option.

ozi.cc
reference/route/ozi~rte.rte [new file with mode: 0644]
reference/track/20070813_short.plt [new file with mode: 0644]
reference/track/20070813_short~plt.gpx [new file with mode: 0644]
reference/track/20070813_short~plt.plt [new file with mode: 0644]
testo.d/ozi.test
xmldoc/formats/options/ozi-codec.xml [new file with mode: 0644]

diff --git a/ozi.cc b/ozi.cc
index ef9a147fe83cd74f1dd1857db1f63e053fdb7310..8c3015cb9f2b19376814f636d6eb74f314a923e5 100644 (file)
--- a/ozi.cc
+++ b/ozi.cc
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
 
+    Reference:
+    https://www.oziexplorer4.com/eng/help/fileformats.html
+
+    According to the OZI Explorer developer:
+    "There is no specified character set, it defaults to whatever 8 bit
+     character set "Windows" defaults to - normally CP-1252 but can vary
+     depending on Windows regional settings."
+
+    According to the reference, for some text fields:
+    "comma's not allowed in text fields, character 209 can be used instead
+     and a comma will be substituted."
+    This could work for windows-1252, but not for utf-8.
+    We don't support any special handling for character 209.
+
  */
 
+#include <cctype>               // for tolower
+#include <cmath>                // for lround
+#include <cstdlib>              // for atoi
+
+#include <QtCore/QByteArray>    // for QByteArray
+#include <QtCore/QChar>         // for operator==, QChar
+#include <QtCore/QCharRef>      // for QCharRef
+#include <QtCore/QFile>         // for QFile
+#include <QtCore/QFileInfo>     // for QFileInfo
+#include <QtCore/QFlags>        // for QFlags
+#include <QtCore/QIODevice>     // for operator|, QIODevice::WriteOnly, QIODevice::ReadOnly, QIODevice, QIODevice::OpenModeFlag
+#include <QtCore/QString>       // for QString
+#include <QtCore/QStringList>   // for QStringList
+#include <QtCore/QTextCodec>    // for QTextCodec
+#include <QtCore/QTextStream>   // for QTextStream, operator<<, qSetRealNumberPrecision, QTextStream::FixedNotation
+#include <QtCore/Qt>            // for CaseInsensitive
+#include <QtCore/QtGlobal>      // for qPrintable
+
 #include "defs.h"
-#include "cet_util.h"
-#include "csv_util.h"
-#include "jeeps/gpsmath.h"
-#include <QtCore/QFileInfo>
-#include <cctype>
-#include <cmath>                /* for floor */
-#include <cstdio>
-#include <cstdlib>
+#include "csv_util.h"           // for csv_stringclean
+#include "jeeps/gpsmath.h"      // for GPS_Math_Known_Datum_To_WGS84_M
+#include "src/core/datetime.h"  // for DateTime
+#include "src/core/file.h"      // for File
+
 
 #define MYNAME        "OZI"
 #define BADCHARS       ",\r\n"
 #define DAYS_SINCE_1990        25569
 
-typedef struct {
+struct ozi_fsdata {
   format_specific_data fs;
   int fgcolor;
   int bgcolor;
-} ozi_fsdata;
+};
 
+static struct {
+  gpsbabel::File* file{nullptr};
+  QTextStream* stream{nullptr};
+  QTextCodec* codec{nullptr};
+} ozi_file;
 
-static gbfile* file_in, *file_out;
 static short_handle mkshort_handle;
 static route_head* trk_head;
 static route_head* rte_head;
@@ -69,6 +102,7 @@ static char altunit;
 static char proxunit;
 static double alt_scale;
 static double prox_scale;
+static char* opt_codec;
 
 static
 arglist_t ozi_args[] = {
@@ -112,6 +146,10 @@ arglist_t ozi_args[] = {
     "proxunit", &proxunit_opt, "Unit used in proximity values",
     "miles", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
   },
+  {
+    "codec", &opt_codec, "codec to use for reading and writing strings (default windows-1252)",
+    "windows-1252", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+  },
   ARG_TERMINATOR
 };
 
@@ -119,6 +157,41 @@ static gpsdata_type ozi_objective;
 
 static QString ozi_ofname;
 
+static void
+ozi_open_io(const QString& fname, QIODevice::OpenModeFlag mode)
+{
+  ozi_file.codec = QTextCodec::codecForName(opt_codec);
+  if (ozi_file.codec == nullptr) {
+    fatal(MYNAME ": Unsupported character set '%s'.\n", opt_codec);
+  }
+
+  ozi_file.file = new gpsbabel::File(fname);
+  ozi_file.file->open(mode);
+  ozi_file.stream = new QTextStream(ozi_file.file);
+  ozi_file.stream->setCodec(ozi_file.codec);
+
+  if (mode | QFile::WriteOnly) {
+    ozi_file.stream->setRealNumberNotation(QTextStream::FixedNotation);
+  }
+
+  if (mode | QFile::ReadOnly) {
+    if (ozi_file.codec->mibEnum() == 106) { // UTF-8
+      ozi_file.stream->setAutoDetectUnicode(true);
+    }
+  }
+}
+
+static void
+ozi_close_io()
+{
+  ozi_file.file->close();
+  delete ozi_file.file;
+  ozi_file.file = nullptr;
+  delete ozi_file.stream;
+  ozi_file.stream = nullptr;
+  ozi_file.codec = nullptr;
+}
+
 static void
 ozi_copy_fsdata(ozi_fsdata** dest, ozi_fsdata* src)
 {
@@ -151,15 +224,14 @@ ozi_alloc_fsdata()
   return fsdata;
 }
 
-static void
-ozi_get_time_str(const Waypoint* waypointp, char* buff, gbsize_t buffsz)
+static QString
+ozi_get_time_str(const Waypoint* waypointp)
 {
   if (waypointp->creation_time.isValid()) {
     double time = (waypt_time(waypointp) / SECONDS_PER_DAY) + DAYS_SINCE_1990;
-    snprintf(buff, buffsz, "%.7f", time);
-  } else {
-    *buff = '\0';
+    return QString("%1").arg(time, 0, 'f', 7);
   }
+  return QString("");
 }
 
 static void
@@ -195,15 +267,15 @@ ozi_openfile(const QString& fname)
    */
 
   if (fname == "-") {
-    if (! file_out) {
-      file_out = gbfopen(fname, "wb", MYNAME);
+    if (ozi_file.file == nullptr) {
+      ozi_open_io(fname, QFile::WriteOnly);
     }
     return;
   }
 
   QString buff;
   if ((track_out_count) && (ozi_objective == trkdata)) {
-    buff = QString("-%d").arg(track_out_count);
+    buff = QString("-%1").arg(track_out_count);
   } else {
     buff = QString("");
   }
@@ -219,30 +291,26 @@ ozi_openfile(const QString& fname)
   QString tmpname = QString("%1%2.%3").arg(sname, buff, ozi_extensions[ozi_objective]);
 
   /* re-open file_out with the new filename */
-  if (file_out) {
-    gbfclose(file_out);
-    file_out = nullptr;
+  if (ozi_file.file != nullptr) {
+    ozi_close_io();
   }
-
-  file_out = gbfopen(tmpname, "wb", MYNAME);
+  ozi_open_io(tmpname, QFile::WriteOnly);
 }
 
 static void
 ozi_track_hdr(const route_head* rte)
 {
-  static const char* ozi_trk_header =
-    "OziExplorer Track Point File Version 2.1\r\n"
-    "WGS 84\r\n"
-    "Altitude is in %s\r\n"
-    "Reserved 3\r\n"
-    "0,2,255,%s,0,0,2,8421376\r\n"
-    "0\r\n";
-
   if ((! pack_opt) || (track_out_count == 0)) {
     ozi_openfile(ozi_ofname);
-    gbfprintf(file_out, ozi_trk_header,
-              altunit == 'f' ? "Feet" : "Meters",
-              rte->rte_name.isEmpty() ? "ComplimentsOfGPSBabel" : CSTRc(rte->rte_name));
+    *ozi_file.stream << "OziExplorer Track Point File Version 2.1\r\n"
+                     << "WGS 84\r\n"
+                     << "Altitude is in " << (altunit == 'f' ? "Feet" : "Meters") << "\r\n"
+                     << "Reserved 3\r\n"
+                     << "0,2,255,"
+                     << (rte->rte_name.isEmpty() ? "ComplimentsOfGPSBabel" : rte->rte_name)
+                     << ",0,0,2,8421376\r\n"
+                     << "0\r\n";
   }
 
   track_out_count++;
@@ -253,9 +321,8 @@ static void
 ozi_track_disp(const Waypoint* waypointp)
 {
   double alt;
-  char ozi_time[16];
 
-  ozi_get_time_str(waypointp, ozi_time, sizeof(ozi_time));
+  QString ozi_time = ozi_get_time_str(waypointp);
 
   if (waypointp->altitude == unknown_alt) {
     alt = -777;
@@ -263,22 +330,19 @@ ozi_track_disp(const Waypoint* waypointp)
     alt = waypointp->altitude * alt_scale;
   }
 
-  gbfprintf(file_out, "%.6f,%.6f,%d,%.0f,%s,,\r\n",
-            waypointp->latitude, waypointp->longitude, new_track,
-            alt, ozi_time);
+  *ozi_file.stream << qSetRealNumberPrecision(6) << waypointp->latitude << ','
+                   << waypointp->longitude << ','
+                   << new_track << ','
+                   << qSetRealNumberPrecision(0) << alt << ','
+                   << ozi_time << ",,\r\n";
 
   new_track = 0;
 }
 
-static void
-ozi_track_tlr(const route_head*)
-{
-}
-
 static void
 ozi_track_pr()
 {
-  track_disp_all(ozi_track_hdr, ozi_track_tlr, ozi_track_disp);
+  track_disp_all(ozi_track_hdr, nullptr, ozi_track_disp);
 }
 
 static void
@@ -286,12 +350,10 @@ ozi_route_hdr(const route_head* rte)
 {
   /* prologue on 1st pass only */
   if (route_out_count == 0) {
-               static const char* ozi_route_header =
-                       "OziExplorer Route File Version 1.0\r\n"
-                       "WGS 84\r\n"
-                       "Reserved 1\r\n"
-                       "Reserved 2\r\n";
-    gbfprintf(file_out, ozi_route_header);
+    *ozi_file.stream << "OziExplorer Route File Version 1.0\r\n"
+                     << "WGS 84\r\n"
+                     << "Reserved 1\r\n"
+                     << "Reserved 2\r\n";
   }
 
   route_out_count++;
@@ -309,20 +371,17 @@ ozi_route_hdr(const route_head* rte)
    * R, 1, ICP GALHETA,, 16711680
    */
 
-  gbfprintf(file_out, "R,%d,%s,%s,\r\n",
-            route_out_count,
-            CSTRc(rte->rte_name),
-            CSTRc(rte->rte_desc));
+  *ozi_file.stream << "R," << route_out_count << ','
+                   << rte->rte_name << ','
+                   << rte->rte_desc << ",\r\n";
 }
 
 static void
 ozi_route_disp(const Waypoint* waypointp)
 {
-  char ozi_time[16];
-
   route_wpt_count++;
 
-  ozi_get_time_str(waypointp, ozi_time, sizeof(ozi_time));
+  QString ozi_time = ozi_get_time_str(waypointp);
 
 /*
   double alt;
@@ -353,26 +412,20 @@ ozi_route_disp(const Waypoint* waypointp)
    * W,1,7,7,007,-25.581670,-48.316660,36564.54196,10,1,4,0,65535,TR ILHA GALHETA,0,0
    */
 
-  gbfprintf(file_out, "W,%d,,%d,%s,%.6f,%.6f,%s,0,1,3,0,65535,%s,0,0\r\n",
-            route_out_count,
-            route_wpt_count,
-            CSTR(waypointp->shortname),
-            waypointp->latitude,
-            waypointp->longitude,
-            ozi_time,
-            CSTR(waypointp->description));
+  *ozi_file.stream << "W," << route_out_count << ",,"
+                   << route_wpt_count << ','
+                   << waypointp->shortname << ','
+                   << qSetRealNumberPrecision(6) << waypointp->latitude << ','
+                   << waypointp->longitude << ','
+                   << ozi_time << ",0,1,3,0,65535,"
+                   << waypointp->description << ",0,0\r\n";
 
 }
 
-static void
-ozi_route_tlr(const route_head*)
-{
-}
-
 static void
 ozi_route_pr()
 {
-  route_disp_all(ozi_route_hdr, ozi_route_tlr, ozi_route_disp);
+  route_disp_all(ozi_route_hdr, nullptr, ozi_route_disp);
 }
 
 static void
@@ -415,7 +468,7 @@ ozi_init_units(const int direction) /* 0 = in; 1 = out */
 static void
 rd_init(const QString& fname)
 {
-  file_in = gbfopen(fname, "rb", MYNAME);
+  ozi_open_io(fname, QFile::ReadOnly);
 
   mkshort_handle = mkshort_new_handle();
   ozi_init_units(0);
@@ -424,8 +477,8 @@ rd_init(const QString& fname)
 static void
 rd_deinit()
 {
-  gbfclose(file_in);
-  file_in = nullptr;
+  ozi_close_io();
+
   mkshort_del_handle(&mkshort_handle);
 }
 
@@ -464,18 +517,12 @@ wr_init(const QString& fname)
 
   ozi_init_units(1);
   parse_distance(proximityarg, &proximity, 1 / prox_scale, MYNAME);
-
-  file_out = nullptr;
 }
 
 static void
 wr_deinit()
 {
-  if (file_out != nullptr) {
-
-    gbfclose(file_out);
-    file_out = nullptr;
-  }
+  ozi_close_io();
   ozi_ofname.clear();
 
   mkshort_del_handle(&mkshort_handle);
@@ -577,26 +624,24 @@ ozi_parse_waypt(int field, const QString& str, Waypoint* wpt_tmp, ozi_fsdata* fs
 }
 
 static void
-ozi_parse_track(int field, char* str, Waypoint* wpt_tmp, char* trk_name)
+ozi_parse_track(int field, const QString& str, Waypoint* wpt_tmp, char* trk_name)
 {
-  double alt;
-
-  if (*str == '\0') {
+  if (str.isEmpty()) {
     return;
   }
 
   switch (field) {
   case 0:
     /* latitude */
-    wpt_tmp->latitude = atof(str);
+    wpt_tmp->latitude = str.toDouble();
     break;
   case 1:
     /* longitude */
-    wpt_tmp->longitude = atof(str);
+    wpt_tmp->longitude = str.toDouble();
     break;
   case 2:
     /* new track flag */
-    if ((atoi(str) == 1) && (trk_head->rte_waypt_ct > 0)) {
+    if ((str.toInt() == 1) && (trk_head->rte_waypt_ct > 0)) {
       trk_head = route_head_alloc();
       track_add_head(trk_head);
       if (trk_name) {
@@ -604,15 +649,16 @@ ozi_parse_track(int field, char* str, Waypoint* wpt_tmp, char* trk_name)
       }
     }
     break;
-  case 3:
+  case 3: {
     /* altitude */
-    alt = atof(str);
+    double alt = str.toDouble();
     if (alt == -777) {
       wpt_tmp->altitude = unknown_alt;
     } else {
       wpt_tmp->altitude = alt * alt_scale;
     }
     break;
+  }
   case 4:
     /* DAYS since 1900 00:00:00 in days.days (5.5) */
     ozi_set_time_str(str, wpt_tmp);
@@ -623,9 +669,9 @@ ozi_parse_track(int field, char* str, Waypoint* wpt_tmp, char* trk_name)
 }
 
 static void
-ozi_parse_routepoint(int field, char* str, Waypoint* wpt_tmp)
+ozi_parse_routepoint(int field, const QString& str, Waypoint* wpt_tmp)
 {
-  if (*str == '\0') {
+  if (str.isEmpty()) {
     return;
   }
 
@@ -648,11 +694,11 @@ ozi_parse_routepoint(int field, char* str, Waypoint* wpt_tmp)
     break;
   case 5:
     /* latitude */
-    wpt_tmp->latitude = atof(str);
+    wpt_tmp->latitude = str.toDouble();
     break;
   case 6:
     /* longitude */
-    wpt_tmp->longitude = atof(str);
+    wpt_tmp->longitude = str.toDouble();
     break;
   case 7:
     /* DAYS since 1900 00:00:00 in days.days (5.5) */
@@ -683,7 +729,7 @@ ozi_parse_routepoint(int field, char* str, Waypoint* wpt_tmp)
 }
 
 static void
-ozi_parse_routeheader(int field, const QString& str, Waypoint*)
+ozi_parse_routeheader(int field, const QString& str)
 {
 
   switch (field) {
@@ -719,10 +765,12 @@ data_read()
   char* trk_name = nullptr;
   int linecount = 0;
 
-  while ((buff = gbfgetstr(file_in)), !buff.isNull()) {
-    if ((linecount++ == 0) && file_in->unicode) {
-      cet_convert_init(CET_CHARSET_UTF8, 1);
+  while (true) {
+    buff = ozi_file.stream->readLine();
+    if (buff.isNull()) {
+      break;
     }
+    linecount++;
 
     /*
      * this is particularly nasty.  use the first line of the file
@@ -758,14 +806,9 @@ data_read()
         }
       }
     } else if ((linecount == 5) && (ozi_objective == trkdata)) {
-      int field = 0;
-      char* s = csv_lineparse(CSTR(buff), ",", "", linecount);
-      while (s) {
-        field ++;
-        if (field == 4) {
-          trk_head->rte_name = QString(s).trimmed();
-        }
-        s = csv_lineparse(nullptr, ",", "", linecount);
+      const QStringList parts = buff.split(',');
+      if (parts.size() >= 4) {
+          trk_head->rte_name = parts.at(3).trimmed();
       }
     }
 
@@ -774,20 +817,19 @@ data_read()
       ozi_fsdata* fsdata = ozi_alloc_fsdata();
       Waypoint* wpt_tmp = new Waypoint;
 
-      /* data delimited by commas, possibly enclosed in quotes.  */
-      char* orig_s = xstrdup(CSTR(buff));
-      char* s = csv_lineparse(orig_s, ",", "", linecount);
+      /* data delimited by commas. */
+      const QStringList parts = buff.split(',');
 
       int i = 0;
       bool header = false;
-      while (s) {
+      for (const auto& s : parts) {
         switch (ozi_objective) {
         case trkdata:
           ozi_parse_track(i, s, wpt_tmp, trk_name);
           break;
         case rtedata:
           if (buff[0] == 'R') {
-            ozi_parse_routeheader(i, QString(s), wpt_tmp);
+            ozi_parse_routeheader(i, QString(s));
             header = true;
           } else {
             ozi_parse_routepoint(i, s, wpt_tmp);
@@ -803,9 +845,7 @@ data_read()
           break;
         }
         i++;
-        s = csv_lineparse(nullptr, ",", "", linecount);
       }
-      xfree(orig_s);
 
       switch (ozi_objective) {
       case trkdata:
@@ -861,7 +901,6 @@ ozi_waypt_pr(const Waypoint* wpt)
 {
   static int index = 0;
   double alt;
-  char ozi_time[16];
   QString description;
   QString shortname;
   int faked_fsdata = 0;
@@ -874,7 +913,7 @@ ozi_waypt_pr(const Waypoint* wpt)
     faked_fsdata = 1;
   }
 
-  ozi_get_time_str(wpt, ozi_time, sizeof(ozi_time));
+  QString ozi_time = ozi_get_time_str(wpt);
 
   if (wpt->altitude == unknown_alt) {
     alt = -777;
@@ -911,18 +950,24 @@ ozi_waypt_pr(const Waypoint* wpt)
     icon = wpt->icon_descr.toInt();
   }
 
-  gbfprintf(file_out,
-            "%d,%s,%.6f,%.6f,%s,%d,%d,%d,%d,%d,%s,%d,%d,",
-            index, CSTRc(shortname), wpt->latitude, wpt->longitude, ozi_time, icon,
-            1, 3, fs->fgcolor, fs->bgcolor, CSTRc(description), 0, 0);
+  *ozi_file.stream << index << ','
+                   << shortname << ','
+                   << qSetRealNumberPrecision(6) << wpt->latitude << ','
+                   << wpt->longitude << ','
+                   << ozi_time << ','
+                   << icon << ','
+                   << "1,3,"
+                   << fs->fgcolor << ','
+                   << fs->bgcolor << ','
+                   << description << ",0,0,";
   if (WAYPT_HAS(wpt, proximity) && (wpt->proximity > 0)) {
-    gbfprintf(file_out, "%.1f,", wpt->proximity * prox_scale);
+    *ozi_file.stream << qSetRealNumberPrecision(1) << wpt->proximity * prox_scale << ',';
   } else if (proximity > 0) {
-    gbfprintf(file_out,"%.1f,", proximity * prox_scale);
+    *ozi_file.stream << qSetRealNumberPrecision(1) << proximity * prox_scale << ',';
   } else {
-    gbfprintf(file_out,"%d,", 0);
+    *ozi_file.stream << "0,";
   }
-  gbfprintf(file_out, "%.0f,%d,%d,%d\r\n", alt, 6, 0, 17);
+  *ozi_file.stream << qSetRealNumberPrecision(0) << alt << ",6,0,17\r\n";
 
   if (faked_fsdata) {
     xfree(fs);
@@ -932,18 +977,14 @@ ozi_waypt_pr(const Waypoint* wpt)
 static void
 data_write()
 {
-
   if (waypt_count()) {
-  static const char* ozi_wpt_header =
-    "OziExplorer Waypoint File Version 1.1\r\n"
-    "WGS 84\r\n"
-    "Reserved 2\r\n"
-    "Reserved 3\r\n";
-
-  track_out_count = route_out_count = 0;
+    track_out_count = route_out_count = 0;
     ozi_objective = wptdata;
     ozi_openfile(ozi_ofname);
-    gbfprintf(file_out, ozi_wpt_header);
+    *ozi_file.stream << "OziExplorer Waypoint File Version 1.1\r\n"
+                     << "WGS 84\r\n"
+                     << "Reserved 2\r\n"
+                     << "Reserved 3\r\n";
     waypt_disp_all(ozi_waypt_pr);
   }
 
diff --git a/reference/route/ozi~rte.rte b/reference/route/ozi~rte.rte
new file mode 100644 (file)
index 0000000..e557555
--- /dev/null
@@ -0,0 +1,10 @@
+OziExplorer Route File Version 1.0\r
+WGS 84\r
+Reserved 1\r
+Reserved 2\r
+R,1,1 COSTANERO JA,,\r
+W,1,,1,MPCHIC,-34.445850,-58.523720,40507.8513987,0,1,3,0,65535,MARINA PUNTA CHICA,0,0\r
+W,1,,2,JA12,-34.447380,-58.507270,41344.8282040,0,1,3,0,65535,,0,0\r
+R,2,1 PCHI COLONIA,,\r
+W,2,,1,COLONI,-34.467500,-57.854170,,0,1,3,0,65535,07-OCT-00 18:22,0,0\r
+W,2,,2,COLBO3W ,-34.477470,-57.861670,41154.0041088,0,1,3,0,65535,,0,0\r
diff --git a/reference/track/20070813_short.plt b/reference/track/20070813_short.plt
new file mode 100644 (file)
index 0000000..68e1a49
--- /dev/null
@@ -0,0 +1,50 @@
+OziExplorer Track Point File Version 2.1\r
+WGS 84\r
+Altitude is in Feet\r
+Reserved 3\r
+0,2,255,Vézelay / Cuncy-lès-Varzy          ,1,0,2,8421376,0\r
+2390\r
+  47.466222,   3.747318,0, 1258.8,39307.3279977, 13-août-07, 07:52:19\r
+  47.466150,   3.747233,0, 1236.5,39307.3281134, 13-août-07, 07:52:28\r
+  47.466130,   3.747113,0, 1243.4,39307.3282292, 13-août-07, 07:52:39\r
+  47.466078,   3.747023,0, 1237.5,39307.3282986, 13-août-07, 07:52:44\r
+  47.466020,   3.746888,0, 1221.4,39307.3283681, 13-août-07, 07:52:51\r
+  47.465943,   3.746815,0, 1203.7,39307.3284259, 13-août-07, 07:52:55\r
+  47.465872,   3.746727,0, 1177.2,39307.3284954, 13-août-07, 07:53:02\r
+  47.465902,   3.746598,0, 1168.3,39307.3285532, 13-août-07, 07:53:06\r
+  47.465900,   3.746465,0, 1159.1,39307.3286343, 13-août-07, 07:53:14\r
+  47.465792,   3.746433,0, 1157.8,39307.3287037, 13-août-07, 07:53:20\r
+  47.465713,   3.746388,0, 1154.5,39307.3287616, 13-août-07, 07:53:25\r
+  47.465695,   3.746242,0, 1160.4,39307.3288426, 13-août-07, 07:53:32\r
+  47.465697,   3.746097,0, 1160.1,39307.3289236, 13-août-07, 07:53:38\r
+  47.465717,   3.745948,0, 1169.3,39307.3289815, 13-août-07, 07:53:44\r
+  47.465715,   3.745807,0, 1189.0,39307.3290625, 13-août-07, 07:53:51\r
+  47.465640,   3.745703,0, 1205.0,39307.3291551, 13-août-07, 07:53:59\r
+  47.465575,   3.745608,0, 1203.1,39307.3292130, 13-août-07, 07:54:04\r
+  47.465488,   3.745547,0, 1197.2,39307.3292708, 13-août-07, 07:54:08\r
+  47.465447,   3.745437,0, 1180.8,39307.3293519, 13-août-07, 07:54:16\r
+  47.465353,   3.745390,0, 1157.5,39307.3294213, 13-août-07, 07:54:22\r
+  47.465298,   3.745282,0, 1141.4,39307.3295139, 13-août-07, 07:54:30\r
+  47.465250,   3.745155,0, 1100.1,39307.3296181, 13-août-07, 07:54:39\r
+  47.465157,   3.745133,0, 1101.4,39307.3296759, 13-août-07, 07:54:43\r
+  47.465018,   3.745113,0, 1094.5,39307.3297569, 13-août-07, 07:54:50\r
+  47.465010,   3.744965,0, 1101.7,39307.3298148, 13-août-07, 07:54:55\r
+  47.464992,   3.744823,0, 1114.2,39307.3299074, 13-août-07, 07:55:03\r
+  47.464955,   3.744658,0, 1124.3,39307.3300000, 13-août-07, 07:55:12\r
+  47.464885,   3.744563,0, 1130.6,39307.3300810, 13-août-07, 07:55:18\r
+  47.464778,   3.744497,0, 1117.1,39307.3301505, 13-août-07, 07:55:25\r
+  47.464710,   3.744412,0, 1109.2,39307.3302083, 13-août-07, 07:55:29\r
+  47.464635,   3.744317,0, 1110.9,39307.3302778, 13-août-07, 07:55:36\r
+  47.464547,   3.744233,0, 1109.6,39307.3303472, 13-août-07, 07:55:41\r
+  47.464475,   3.744142,0, 1112.8,39307.3304282, 13-août-07, 07:55:48\r
+  47.464405,   3.744057,0, 1101.7,39307.3304861, 13-août-07, 07:55:53\r
+  47.464342,   3.743965,0, 1098.1,39307.3305440, 13-août-07, 07:55:59\r
+  47.464313,   3.743808,0, 1094.8,39307.3306366, 13-août-07, 07:56:07\r
+  47.464233,   3.743725,0, 1077.7,39307.3307292, 13-août-07, 07:56:15\r
+  47.464173,   3.743628,0, 1066.6,39307.3308102, 13-août-07, 07:56:22\r
+  47.464102,   3.743548,0, 1064.9,39307.3308912, 13-août-07, 07:56:29\r
+  47.464015,   3.743477,0, 1049.9,39307.3309491, 13-août-07, 07:56:34\r
+  47.463935,   3.743393,0, 1045.3,39307.3310301, 13-août-07, 07:56:41\r
+  47.463860,   3.743303,0, 1063.3,39307.3311343, 13-août-07, 07:56:50\r
+  47.463847,   3.743163,0, 1062.7,39307.3312037, 13-août-07, 07:56:56\r
+  47.463833,   3.743018,0, 1072.5,39307.3312731, 13-août-07, 07:57:01\r
diff --git a/reference/track/20070813_short~plt.gpx b/reference/track/20070813_short~plt.gpx
new file mode 100644 (file)
index 0000000..149e606
--- /dev/null
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.0" creator="GPSBabel - http://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/0">
+  <time>1970-01-01T00:00:00Z</time>
+  <bounds minlat="47.463833000" minlon="3.743018000" maxlat="47.466222000" maxlon="3.747318000"/>
+  <trk>
+    <name>Vézelay / Cuncy-lès-Varzy</name>
+    <trkseg>
+      <trkpt lat="47.466222000" lon="3.747318000">
+        <ele>383.682</ele>
+        <time>2007-08-13T07:52:19.328Z</time>
+      </trkpt>
+      <trkpt lat="47.466150000" lon="3.747233000">
+        <ele>376.885</ele>
+        <time>2007-08-13T07:52:28.328Z</time>
+      </trkpt>
+      <trkpt lat="47.466130000" lon="3.747113000">
+        <ele>378.988</ele>
+        <time>2007-08-13T07:52:39.328Z</time>
+      </trkpt>
+      <trkpt lat="47.466078000" lon="3.747023000">
+        <ele>377.190</ele>
+        <time>2007-08-13T07:52:44.328Z</time>
+      </trkpt>
+      <trkpt lat="47.466020000" lon="3.746888000">
+        <ele>372.283</ele>
+        <time>2007-08-13T07:52:51.328Z</time>
+      </trkpt>
+      <trkpt lat="47.465943000" lon="3.746815000">
+        <ele>366.888</ele>
+        <time>2007-08-13T07:52:55.328Z</time>
+      </trkpt>
+      <trkpt lat="47.465872000" lon="3.746727000">
+        <ele>358.811</ele>
+        <time>2007-08-13T07:53:02.328Z</time>
+      </trkpt>
+      <trkpt lat="47.465902000" lon="3.746598000">
+        <ele>356.098</ele>
+        <time>2007-08-13T07:53:06.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465900000" lon="3.746465000">
+        <ele>353.294</ele>
+        <time>2007-08-13T07:53:14.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465792000" lon="3.746433000">
+        <ele>352.897</ele>
+        <time>2007-08-13T07:53:19.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465713000" lon="3.746388000">
+        <ele>351.892</ele>
+        <time>2007-08-13T07:53:25.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465695000" lon="3.746242000">
+        <ele>353.690</ele>
+        <time>2007-08-13T07:53:32.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465697000" lon="3.746097000">
+        <ele>353.598</ele>
+        <time>2007-08-13T07:53:38.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465717000" lon="3.745948000">
+        <ele>356.403</ele>
+        <time>2007-08-13T07:53:44.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465715000" lon="3.745807000">
+        <ele>362.407</ele>
+        <time>2007-08-13T07:53:51.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465640000" lon="3.745703000">
+        <ele>367.284</ele>
+        <time>2007-08-13T07:53:59.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465575000" lon="3.745608000">
+        <ele>366.705</ele>
+        <time>2007-08-13T07:54:04.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465488000" lon="3.745547000">
+        <ele>364.907</ele>
+        <time>2007-08-13T07:54:08.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465447000" lon="3.745437000">
+        <ele>359.908</ele>
+        <time>2007-08-13T07:54:16.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465353000" lon="3.745390000">
+        <ele>352.806</ele>
+        <time>2007-08-13T07:54:22.329Z</time>
+      </trkpt>
+      <trkpt lat="47.465298000" lon="3.745282000">
+        <ele>347.899</ele>
+        <time>2007-08-13T07:54:30.330Z</time>
+      </trkpt>
+      <trkpt lat="47.465250000" lon="3.745155000">
+        <ele>335.310</ele>
+        <time>2007-08-13T07:54:39.330Z</time>
+      </trkpt>
+      <trkpt lat="47.465157000" lon="3.745133000">
+        <ele>335.707</ele>
+        <time>2007-08-13T07:54:43.330Z</time>
+      </trkpt>
+      <trkpt lat="47.465018000" lon="3.745113000">
+        <ele>333.604</ele>
+        <time>2007-08-13T07:54:50.330Z</time>
+      </trkpt>
+      <trkpt lat="47.465010000" lon="3.744965000">
+        <ele>335.798</ele>
+        <time>2007-08-13T07:54:55.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464992000" lon="3.744823000">
+        <ele>339.608</ele>
+        <time>2007-08-13T07:55:03.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464955000" lon="3.744658000">
+        <ele>342.687</ele>
+        <time>2007-08-13T07:55:12.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464885000" lon="3.744563000">
+        <ele>344.607</ele>
+        <time>2007-08-13T07:55:18.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464778000" lon="3.744497000">
+        <ele>340.492</ele>
+        <time>2007-08-13T07:55:25.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464710000" lon="3.744412000">
+        <ele>338.084</ele>
+        <time>2007-08-13T07:55:29.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464635000" lon="3.744317000">
+        <ele>338.602</ele>
+        <time>2007-08-13T07:55:36.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464547000" lon="3.744233000">
+        <ele>338.206</ele>
+        <time>2007-08-13T07:55:41.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464475000" lon="3.744142000">
+        <ele>339.181</ele>
+        <time>2007-08-13T07:55:48.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464405000" lon="3.744057000">
+        <ele>335.798</ele>
+        <time>2007-08-13T07:55:53.330Z</time>
+      </trkpt>
+      <trkpt lat="47.464342000" lon="3.743965000">
+        <ele>334.701</ele>
+        <time>2007-08-13T07:55:59.331Z</time>
+      </trkpt>
+      <trkpt lat="47.464313000" lon="3.743808000">
+        <ele>333.695</ele>
+        <time>2007-08-13T07:56:07.331Z</time>
+      </trkpt>
+      <trkpt lat="47.464233000" lon="3.743725000">
+        <ele>328.483</ele>
+        <time>2007-08-13T07:56:15.331Z</time>
+      </trkpt>
+      <trkpt lat="47.464173000" lon="3.743628000">
+        <ele>325.100</ele>
+        <time>2007-08-13T07:56:22.331Z</time>
+      </trkpt>
+      <trkpt lat="47.464102000" lon="3.743548000">
+        <ele>324.582</ele>
+        <time>2007-08-13T07:56:28.331Z</time>
+      </trkpt>
+      <trkpt lat="47.464015000" lon="3.743477000">
+        <ele>320.010</ele>
+        <time>2007-08-13T07:56:34.331Z</time>
+      </trkpt>
+      <trkpt lat="47.463935000" lon="3.743393000">
+        <ele>318.607</ele>
+        <time>2007-08-13T07:56:41.331Z</time>
+      </trkpt>
+      <trkpt lat="47.463860000" lon="3.743303000">
+        <ele>324.094</ele>
+        <time>2007-08-13T07:56:50.331Z</time>
+      </trkpt>
+      <trkpt lat="47.463847000" lon="3.743163000">
+        <ele>323.911</ele>
+        <time>2007-08-13T07:56:55.331Z</time>
+      </trkpt>
+      <trkpt lat="47.463833000" lon="3.743018000">
+        <ele>326.898</ele>
+        <time>2007-08-13T07:57:01.331Z</time>
+      </trkpt>
+    </trkseg>
+  </trk>
+</gpx>
diff --git a/reference/track/20070813_short~plt.plt b/reference/track/20070813_short~plt.plt
new file mode 100644 (file)
index 0000000..e1d6d2a
--- /dev/null
@@ -0,0 +1,50 @@
+OziExplorer Track Point File Version 2.1\r
+WGS 84\r
+Altitude is in Feet\r
+Reserved 3\r
+0,2,255,Vézelay / Cuncy-lès-Varzy,0,0,2,8421376\r
+0\r
+47.466222,3.747318,1,1259,39307.3280015,,\r
+47.466150,3.747233,0,1236,39307.3281056,,\r
+47.466130,3.747113,0,1243,39307.3282330,,\r
+47.466078,3.747023,0,1237,39307.3282908,,\r
+47.466020,3.746888,0,1221,39307.3283719,,\r
+47.465943,3.746815,0,1204,39307.3284181,,\r
+47.465872,3.746727,0,1177,39307.3284992,,\r
+47.465902,3.746598,0,1168,39307.3285455,,\r
+47.465900,3.746465,0,1159,39307.3286381,,\r
+47.465792,3.746433,0,1158,39307.3286959,,\r
+47.465713,3.746388,0,1155,39307.3287654,,\r
+47.465695,3.746242,0,1160,39307.3288464,,\r
+47.465697,3.746097,0,1160,39307.3289158,,\r
+47.465717,3.745948,0,1169,39307.3289853,,\r
+47.465715,3.745807,0,1189,39307.3290663,,\r
+47.465640,3.745703,0,1205,39307.3291589,,\r
+47.465575,3.745608,0,1203,39307.3292168,,\r
+47.465488,3.745547,0,1197,39307.3292631,,\r
+47.465447,3.745437,0,1181,39307.3293557,,\r
+47.465353,3.745390,0,1158,39307.3294251,,\r
+47.465298,3.745282,0,1141,39307.3295177,,\r
+47.465250,3.745155,0,1100,39307.3296219,,\r
+47.465157,3.745133,0,1101,39307.3296682,,\r
+47.465018,3.745113,0,1095,39307.3297492,,\r
+47.465010,3.744965,0,1102,39307.3298071,,\r
+47.464992,3.744823,0,1114,39307.3298997,,\r
+47.464955,3.744658,0,1124,39307.3300038,,\r
+47.464885,3.744563,0,1131,39307.3300733,,\r
+47.464778,3.744497,0,1117,39307.3301543,,\r
+47.464710,3.744412,0,1109,39307.3302006,,\r
+47.464635,3.744317,0,1111,39307.3302816,,\r
+47.464547,3.744233,0,1110,39307.3303395,,\r
+47.464475,3.744142,0,1113,39307.3304205,,\r
+47.464405,3.744057,0,1102,39307.3304784,,\r
+47.464342,3.743965,0,1098,39307.3305478,,\r
+47.464313,3.743808,0,1095,39307.3306404,,\r
+47.464233,3.743725,0,1078,39307.3307330,,\r
+47.464173,3.743628,0,1067,39307.3308140,,\r
+47.464102,3.743548,0,1065,39307.3308835,,\r
+47.464015,3.743477,0,1050,39307.3309529,,\r
+47.463935,3.743393,0,1045,39307.3310339,,\r
+47.463860,3.743303,0,1063,39307.3311381,,\r
+47.463847,3.743163,0,1063,39307.3311960,,\r
+47.463833,3.743018,0,1073,39307.3312654,,\r
index b7774ecfa306e9db5f235af91693145d44212b3d..986c58ff0fc1ab0041d6d511c17b1ad2e26a6f11 100644 (file)
@@ -8,3 +8,12 @@ compare ${TMPDIR}/ozi.wpt ${REFERENCE}
 # Test Ozi routes.
 gpsbabel -i ozi -f ${REFERENCE}/route/ozi.rte -o gpx -F ${TMPDIR}/ozi~gpx.gpx
 compare ${TMPDIR}/ozi~gpx.gpx ${REFERENCE}/route/
+gpsbabel -i ozi -f ${REFERENCE}/route/ozi.rte -o ozi -F ${TMPDIR}/ozi~rte.rte
+compare ${REFERENCE}/route/ozi~rte.rte ${TMPDIR}/ozi~rte.rte
+
+# Test Ozi tracks.
+gpsbabel -i ozi -f ${REFERENCE}/track/20070813_short.plt -o gpx -F ${TMPDIR}/20070813_short~plt.gpx
+compare ${REFERENCE}/track/20070813_short~plt.gpx ${TMPDIR}/20070813_short~plt.gpx
+gpsbabel -i ozi -f ${REFERENCE}/track/20070813_short.plt -o ozi -F ${TMPDIR}/20070813_short~plt.plt
+compare ${REFERENCE}/track/20070813_short~plt.plt ${TMPDIR}/20070813_short~plt.plt
+
diff --git a/xmldoc/formats/options/ozi-codec.xml b/xmldoc/formats/options/ozi-codec.xml
new file mode 100644 (file)
index 0000000..b586abc
--- /dev/null
@@ -0,0 +1,5 @@
+<para>
+This lets you override the default codec of 'windows-1252'.  As an
+input option the codec should correspond to the encoding of the input file.
+As an output option it sets the encoding of the output file.
+</para>